# -*- coding: utf-8 -*-
import re
import md5
import datetime
from django.db import models
from django.contrib.auth.models import User
from django.template.loader import render_to_string
from django.core.mail import send_mail
from lr import settings
from lr.message.models import LrMessage
from lr.bank.models import Bank
from lr.cabinet.models import ViewOptions, UserRels
import random
from lr.bookcase.models import Bookcase


class RegException(Exception):
    """ Ошибка в процессе регистрации нового юзера """
    pass



class RegistrationManager(models.Manager):
    """ Менеджер регистрации новых юзеров """
    
    
    def create_user(self, username, password, invite, email, nickname=None,
                    first_name=None, patronymic=None, last_name=None, birthday=None,
                    gender=u'x', userpic=None, send_email=True):
        """ Создать нового юзера """
        
        if invite:
            # Найти пригласившего
            keys = re.search(ur'^(?P<k0>[0-9\u0041-\u0046]{4})\-(?P<k1>[0-9\u0041-\u0046]{4})\-(?P<k2>[0-9\u0041-\u0046]{4})\-(?P<k3>[0-9\u0041-\u0046]{4})$',
                             invite)
            if keys:
                k0 = int('0x' + keys.group('k0'), 16)
                k1 = int('0x' + keys.group('k1'), 16)
                k2 = int('0x' + keys.group('k2'), 16)
                k3 = int('0x' + keys.group('k3'), 16)
            else:
                raise RegException(u'Неверный формат кода приглашения.')
            
            # Проверка приглашения и логина на случай, если за время регистрации
            # ими воспользовался кто-то другой
            invites = Invite.objects.filter(key0=k0, key1=k1, key2=k2, key3=k3).only('owner')
            if len(invites) == 0:
                raise RegException(u'Извините, ваше приглашение уже использовано.')
            
            invite = invites[0]
            inviting = invite.owner
            rank = 1
            invite.delete()
        else:
            inviting = None
            rank = 0
        
        users = User.objects.filter(username=username).only('username')
        if len(users) > 0:
            raise RegException(u'Извините, пользователь с логином <b>%s</b> уже зарегистрирован.' % username)
        
        # Создать нового пользователя
        new_user = User.objects.create_user(username, email, password)
        new_user.first_name = first_name
        new_user.last_name = last_name
        new_user.save()
        
        # Создать профиль нового пользователя
        self.create_profile(inviting=inviting, user=new_user, patronymic=patronymic, nickname=nickname,
                            birthday=birthday, gender=gender, userpic=userpic, rank=rank)
        
        # Создать набор отношений к другим пользователям
        UserRels.objects.create_user_rels(user=new_user)
        
        # Создать настройки отображения нового пользователя
        ViewOptions.objects.create_view_options(user=new_user)
        
        # Создать банк нового пользователя
        Bank.objects.create_bank(user=new_user)
        
        # Создать книжный шкаф для нового пользователя
        Bookcase.objects.create_bookcase(user=new_user)
        
        if rank > 0:
            # Создать указанное в настройках количество приглашений для нового пользователя,
            # Приглашения выдаются только приглашенным пользователям
            x = settings.NUM_INVITES_FOR_NEW_USER
            while x > 0:
                Invite.objects.create_invite(new_user)
                x -= 1;
        
        if send_email:
            # Сформировать письмо для подтверждения аккаунта
            confirm = self.create_confirm(new_user)
            
            mail_subject = render_to_string('messages/registration_email_subject.html',
                                            { 'site': settings.SITE_URL })
            mail_subject = ''.join(mail_subject.splitlines())
            mail_message = render_to_string('messages/registration_email.html',
                                            { 'activation_key': u'%s/confirm?code=%s' % (settings.SITE_URL, confirm.confirm),
                                              'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
                                              'site': settings.SITE_URL })
            system_message_title = u'Аккаунт успешно создан'
            system_message = render_to_string('messages/registration_email_message.html',
                                              { 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS })
            
            send_mail(mail_subject, mail_message, settings.DEFAULT_FROM_EMAIL, [email])
        else:
            # Аккаунт считается подтвержденным
            system_message_title = u'Регистрация завершена'
            system_message = render_to_string('messages/registration_message.html')
        
        # Создать системное сообщение об успешной регистрации
        LrMessage.objects.create_message(new_user, system_message_title, system_message)
        return new_user
    
    
    def create_profile(self, inviting, user, patronymic=None, nickname=None,
                       birthday=None, gender=u'x', userpic=None, rank=0):
        """ Создать новый профиль пользователя """
        new_profile = self.create(inviting=inviting, user=user, patronymic=patronymic,
                                  nickname=nickname, birthday=birthday, gender=gender,
                                  userpic=userpic, rank=rank, presentation='login', sign='login',
                                  about=u'')
        return new_profile
    
    
    def create_confirm(self, user):
        """ Создать код подтверждения """
        code = md5.md5(user.username + '#' + user.email).hexdigest().decode('utf-8')
        new_confirm = Confirm(user=user, confirm=code,
                              timestamp=datetime.datetime.now())
        new_confirm.save()
        return new_confirm



class InviteManager(models.Manager):
    """ Менеджер создания новых приглашений """
    
    def create_invite(self, owner):
        """ Создать новое приглашение """
        while True:
            k0 = random.randint(0, 0xFFFF)
            k1 = random.randint(0, 0xFFFF)
            k2 = random.randint(0, 0xFFFF)
            k3 = random.randint(0, 0xFFFF)
            
            if len(Invite.objects.filter(key0=k0, key1=k1, key2=k2, key3=k3)) == 0:
                new_invite = self.create(key0=k0, key1=k1, key2=k2, key3=k3, owner=owner)
                return new_invite



class Invite(models.Model):
    """ Код приглашения """
    key0 = models.IntegerField()
    key1 = models.IntegerField()
    key2 = models.IntegerField()
    key3 = models.IntegerField()
    owner = models.ForeignKey(User)
    
    objects = InviteManager()
    
    
    class Meta:
        verbose_name = u'Приглашение'
        verbose_name_plural = u'Приглашения'
    
    
    def __unicode__(self):
        """ Имя поля в админке """
        return u'%.4X-%.4X-%.4X-%.4X' % (self.key0, self.key1, self.key2, self.key3)



class Confirm(models.Model):
    """ Код подтверждения аккаунта """
    user = models.OneToOneField(User, related_name='confirm_user', verbose_name=u'Пользователь', primary_key=True)
    confirm = models.CharField(max_length=126, verbose_name=u'Код подтверждения аккаунта')
    timestamp = models.DateTimeField(verbose_name=u'Дата создания')
    
    
    class Meta:
        verbose_name = u'Код подтверждения'
        verbose_name_plural = u'Коды подтверждения'
    
    
    def __unicode__(self):
        """ Имя в поле админки """
        return u'%s (%s)' % (self.confirm, self.user.username)



class Profile(models.Model):
    """ Пользовательский профиль """
    
    
    GENDER_CHOICES = ((u'x', u'Нет'), (u'm', u'Муж.'), (u'f', u'Жен.'))
    
    PRESENT_CHOICES = ((u'login', u'Логин'), (u'long_name', u'Фамилия Имя Отчество'), \
                       (u'short_name', u'Фамилия Имя'), (u'family', u'Фамилия'), \
                       (u'nick', u'Псевдоним'))
    
    SIGN_CHOICES = ((u'login', u'Логин'), (u'long_name', u'Фамилия Имя Отчество'), \
                    (u'long_name_short', u'Фамилия И. О.'), (u'short_name', u'Фамилия Имя'), \
                    (u'short_name_rev', u'Имя Фамилия'), (u'family', u'Фамилия'), \
                    (u'nick', u'Псевдоним'))
    
    user = models.OneToOneField(User, related_name='profile_user', verbose_name=u'Пользователь', primary_key=True)
    patronymic = models.CharField(max_length=30, blank=True, verbose_name=u'Отчество')
    nickname = models.CharField(max_length=30, blank=True, null=True, verbose_name=u'Псевдоним')
    birthday = models.DateField(blank=True, null=True, verbose_name=u'День рожденья')
    gender = models.CharField(max_length=2, choices=GENDER_CHOICES, default=u'x', verbose_name=u'Пол')
    userpic = models.ImageField(upload_to='static/userpic', blank=True, null=True, verbose_name=u'Фоторгафия')
    inviting = models.ForeignKey(User, blank=True, null=True, verbose_name=u'Пригласивший', related_name='inviting_user')
    presentation = models.CharField(max_length=10, choices=PRESENT_CHOICES, default=u'login', verbose_name=u'Представление')
    sign = models.CharField(max_length=15, choices=SIGN_CHOICES, default=u'login', verbose_name=u'Подпись')
    about = models.TextField(blank=True, null=True, verbose_name=u'О себе')
    rank = models.IntegerField(default=0, verbose_name=u'Ранг')
    
    objects = RegistrationManager()
    
    
    def is_empty(self):
        """ Вспомогательный метод, возвращает True, если профиль является пустышкой """
        return False
    
    
    def is_writer(self):
        """ Вспомогательный метод, возвращает True, если пользователь имеет статус автора """
        return self.rank > 0
    
    
    def is_jour(self):
        """ Вспомогательный метод, возвращает True, если пользователь может брать персональные заказы """
        return self.rank > 1
    
    
    class Meta:
        verbose_name = u'Профиль пользователя'
        verbose_name_plural = u'Профили пользователей'
    
    
    def __unicode__(self):
        """ Имя поля в админке """
        name = self.user.last_name
        if name and self.user.first_name:
            name = u'%s %s' % (self.user.first_name, name)
        nick = self.nickname or name
        if nick:
            return u'%s (%s)' % (nick, self.user.username)
        return self.user.username
    
    
    
    @staticmethod
    def get_user_name(user, profile=None, sign=False):
        """ Статическая функция, возвращающая имя пользователя в системе """
        try:
            if not profile or profile.is_empty():
                profile = Profile.objects.get(user=user)
            
            data = {'name': user.username, 'first_name': user.first_name,
                    'last_name': user.last_name, 'patronymic': profile.patronymic,
                    'nickname': profile.nickname}
            if sign:
                name = Profile.make_sign(data, profile.sign)
            else:
                name = Profile.make_sign(data, profile.presentation)
            gender = profile.gender
        except:
            name = user.username
            gender = u'x'
        
        return (name, gender)
    
    
    
    @staticmethod
    def make_sign(data, key):
        """ Вспомогательная функция, формирующая представление пользователя """
        name = data['name']
        first_name = data['first_name']
        last_name = data['last_name']
        patronymic = data['patronymic']
        nickname = data['nickname']
        
        if key == 'login':
            return name
        elif key == 'long_name':
            if first_name and last_name and patronymic:
                return u'%s %s %s' % (last_name, first_name, patronymic)
        elif key == 'long_name_short':
            if first_name and last_name and patronymic:
                return u'%s %s. %s.' % (last_name, first_name[0], patronymic[0])
        elif key == 'short_name':
            if first_name and last_name:
                return u'%s %s' % (last_name, first_name)
        elif key == 'short_name_rev':
            if first_name and last_name:
                return u'%s %s' % (first_name, last_name)
        elif key == 'family':
            if last_name:
                return last_name
        elif key == 'nick':
            if nickname:
                return nickname
        return name




class AnonymousProfile(object):
    """ Класс-пустышка, имитирующий поведение Profile-а """
    
    user = u''
    patronymic = u''
    nickname = u''
    birthday = None
    gender = u'x'
    userpic = None
    inviting = None
    presentation = u'login'
    sign = u'login'
    about = u''
    rank = 0
    
    
    def __setattr__(self, name, value):
        raise Exception('AnonymousProfile is readonly dummy object, cannot change the %s field.' % name)
    
    
    def __init__(self):
        pass


    def __unicode__(self):
        return 'AnonymousProfile'


    def __str__(self):
        return unicode(self).encode('utf-8')
    
    
    def __eq__(self, other):
        return isinstance(other, self.__class__)
    
    
    def __ne__(self, other):
        return not self.__eq__(other)
    
    
    def __hash__(self):
        return 1 # instances always return the same hash value
   
    
    def is_empty(self):
        """ Вспомогательный метод, возвращает True, если профиль является пустышкой """
        return True
    
    
    def is_writer(self):
        """ Вспомогательный метод, возвращает True, если пользователь имеет статус автора """
        return False
    
    
    def is_jour(self):
        """ Вспомогательный метод, возвращает True, если пользователь может брать персональные заказы """
        return False
    